//go:build linux
// +build linux

package filecopy

import (
	"fmt"
	"os"
	"syscall"
	"time"
)

// CopyWithSendfile 使用内核 sendfile 完成文件复制。
func CopyWithSendfile(srcPath, dstPath string) error {
	start := time.Now()

	src, err := os.Open(srcPath)
	if err != nil {
		return fmt.Errorf("open src: %w", err)
	}
	defer src.Close()

	info, err := src.Stat()
	if err != nil {
		return fmt.Errorf("stat src: %w", err)
	}
	size := info.Size()
	if size == 0 {
		dst, err := os.Create(dstPath)
		if err != nil {
			return fmt.Errorf("create empty dst: %w", err)
		}
		return dst.Close()
	}

	dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
	if err != nil {
		return fmt.Errorf("open dst: %w", err)
	}
	defer dst.Close()

	outFD := int(dst.Fd())
	inFD := int(src.Fd())
	remaining := size
	const maxChunk = 1 << 30

	for remaining > 0 {
		chunk := remaining
		if chunk > maxChunk {
			chunk = maxChunk
		}
		n, err := syscall.Sendfile(outFD, inFD, nil, int(chunk))
		if err != nil {
			return fmt.Errorf("sendfile: %w", err)
		}
		if n == 0 {
			break
		}
		remaining -= int64(n)
	}

	if remaining != 0 {
		return fmt.Errorf("sendfile incomplete: remaining=%d", remaining)
	}

	if err := dst.Sync(); err != nil {
		return fmt.Errorf("sync dst: %w", err)
	}

	_ = start
	return nil
}
